/*
 * systrace - Should be a tool to analyze system call traces but can be used
 *            to analyze sequences of generic strings
 *
 * Copyright (c) 2011 Matteo Cicuttin
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

#include <iostream>
#include <sstream>
#include <vector>

#include "GraphAnalysis.h"


GraphAnalysis::GraphAnalysis()
{
	set_module_name("graph");
	_oformat = HUMAN_FORMAT;
}

void
GraphAnalysis::add_sample(const string& s)
{
	if (_prev == "")
	{
		_prev = s;
		return;
	}
	
	_graph[_prev].insert(s);
	_prev = s;
}

void
GraphAnalysis::print_result(ostream& os)
{
	switch (_oformat)
	{
		case HUMAN_FORMAT:
			print_human(os);
			break;
			
		case GV_FORMAT:
			print_gv(os);
			break;
	}
}

void
GraphAnalysis::print_human(ostream &os)
{
	map<string, set<string> >::iterator itor;
	for (itor = _graph.begin(); itor != _graph.end(); itor++)
	{
		os << "\x1B[1;31m" << (*itor).first << ":\x1B[0m ";
		
		set<string>& s = (*itor).second;
		set<string>::iterator s_itor;
		for (s_itor = s.begin(); s_itor != s.end(); s_itor++)
			os << (*s_itor) << " ";
		os << endl;
	}
}

void
GraphAnalysis::print_gv(ostream& os)
{
	os << "digraph syscall_tran {" << endl;
	os << "\tnode [shape = circle];" << endl;
	
	map<string, set<string> >::iterator itor;
	for (itor = _graph.begin(); itor != _graph.end(); itor++)
	{
		set<string>& s = (*itor).second;
		set<string>::iterator s_itor;
		for (s_itor = s.begin(); s_itor != s.end(); s_itor++)
			os << "\t\"" << (*itor).first << "\" -> \"" << (*s_itor) << "\";" << endl;
	}
	
	os << "}" << endl;
}

void
GraphAnalysis::flush(void)
{
	_graph.clear();
}

void
GraphAnalysis::help(void)
{
	cout << "  \x1B[1mprint [name]\x1B[0m         print results" << endl;
	cout << "  \x1B[1mfprint <filename>\x1B[0m    print results to file" << endl;
	cout << "  \x1B[1moformat <fmt>\x1B[0m        select output type, where fmt is one of the following:" << endl;
	cout << "     \x1B[1mhuman\x1B[0m                human-readable output" << endl;
	cout << "     \x1B[1mgraphviz\x1B[0m             graphviz script to plot data" << endl;
	cout << "  \x1B[1menable\x1B[0m               enable module" << endl;
	cout << "  \x1B[1mdisable\x1B[0m              disable module" << endl;
	cout << "  \x1B[1mexit\x1B[0m                 return to parent context" << endl;
	cout << "  \x1B[1mflush\x1B[0m                flush data structures" << endl;
}

void
GraphAnalysis::run_interactive(void)
{
	while (1)
	{
		stringstream oss;
		vector<string> tokens;
		char *s, *pch;
		
		oss << "\x1B[1;33m" << module_name() << " >>\x1B[0m ";
		
		s = readline(oss.str().c_str());
		add_history(s);
		
		pch = strtok(s, " ");
		while (pch)
		{
			tokens.push_back(pch);
			pch = strtok(NULL, " ");
		}
		
		if ( tokens.size() == 0 )
			continue;
		
		if ( tokens.at(0) == "help" )
			help();
		
		if ( tokens.at(0) == "print" )
			print_result(cout);
		
		if ( tokens.at(0) == "fprint" )
		{
			if (tokens.size() == 2)
				fprint( tokens.at(1) );
			else
				cout << "Specify output file name!" << endl;
		}
		
		if ( tokens.at(0) == "enable" )
			set_enabled(true);
		
		if ( tokens.at(0) == "disable" )
			set_enabled(false);
		
		if ( tokens.at(0) == "exit" )
			break;
		
		if ( tokens.at(0) == "flush" )
			flush();
		
		if ( tokens.at(0) == "oformat" )
		{
			if (tokens.size() == 2)
			{
				if ( tokens.at(1) == "graphviz" )
					_oformat = GV_FORMAT;
				if ( tokens.at(1) == "human" )
					_oformat = HUMAN_FORMAT;
			}
			else
				cout << "Specify output format!" << endl;
		}
		
		tokens.clear();
		free(s);
	}
}